import path from "node:path";
import { cancel, isCancel, log, select, text } from "@clack/prompts";
import consola from "consola";
import { execa } from "execa";
import fs from "fs-extra";
import pc from "picocolors";
import type { ProjectConfig } from "../../types";
import { commandExists } from "../../utils/command-exists";
import { exitCancelled } from "../../utils/errors";
import { addEnvVariablesToFile, type EnvVariable } from "../core/env-setup";

type MongoDBConfig = {
  connectionString: string;
};

async function checkAtlasCLI() {
  try {
    const exists = await commandExists("atlas");
    if (exists) {
      log.info("MongoDB Atlas CLI found");
    } else {
      log.warn(pc.yellow("MongoDB Atlas CLI not found"));
    }
    return exists;
  } catch {
    log.error(pc.red("Error checking MongoDB Atlas CLI"));
    return false;
  }
}

async function initMongoDBAtlas(serverDir: string) {
  try {
    const hasAtlas = await checkAtlasCLI();

    if (!hasAtlas) {
      consola.error(pc.red("MongoDB Atlas CLI not found."));
      log.info(
        pc.yellow(
          "Please install it from: https://www.mongodb.com/docs/atlas/cli/current/install-atlas-cli/",
        ),
      );
      return null;
    }

    log.info("Running MongoDB Atlas setup...");

    await execa("atlas", ["deployments", "setup"], {
      cwd: serverDir,
      shell: true,
      stdio: "inherit",
    });

    log.success("MongoDB Atlas deployment ready");

    const connectionString = await text({
      message: "Enter your MongoDB connection string:",
      placeholder: "mongodb+srv://username:password@cluster.mongodb.net/database",
      validate(value) {
        if (!value) return "Please enter a connection string";
        if (!value.startsWith("mongodb")) {
          return "URL should start with mongodb:// or mongodb+srv://";
        }
      },
    });

    if (isCancel(connectionString)) {
      cancel("MongoDB setup cancelled");
      return null;
    }

    return {
      connectionString: connectionString as string,
    };
  } catch (error) {
    if (error instanceof Error) {
      consola.error(pc.red(error.message));
    }
    return null;
  }
}

async function writeEnvFile(
  projectDir: string,
  backend: ProjectConfig["backend"],
  config?: MongoDBConfig,
) {
  try {
    const targetApp = backend === "self" ? "apps/web" : "apps/server";
    const envPath = path.join(projectDir, targetApp, ".env");
    const variables: EnvVariable[] = [
      {
        key: "DATABASE_URL",
        value: config?.connectionString ?? "mongodb://localhost:27017/mydb",
        condition: true,
      },
    ];
    await addEnvVariablesToFile(envPath, variables);
  } catch {
    consola.error("Failed to update environment configuration");
  }
}

function displayManualSetupInstructions() {
  log.info(`
${pc.green("MongoDB Atlas Manual Setup Instructions:")}

1. Install Atlas CLI:
   ${pc.blue("https://www.mongodb.com/docs/atlas/cli/stable/install-atlas-cli/")}

2. Run the following command and follow the prompts:
   ${pc.blue("atlas deployments setup")}

3. Get your connection string from the Atlas dashboard:
   Format: ${pc.dim("mongodb+srv://USERNAME:PASSWORD@CLUSTER.mongodb.net/DATABASE_NAME")}

4. Add the connection string to your .env file:
   ${pc.dim('DATABASE_URL="your_connection_string"')}
`);
}

export async function setupMongoDBAtlas(config: ProjectConfig, cliInput?: { manualDb?: boolean }) {
  const { projectDir, backend } = config;
  const manualDb = cliInput?.manualDb ?? false;

  const serverDir = path.join(projectDir, "packages/db");
  try {
    await fs.ensureDir(serverDir);

    if (manualDb) {
      log.info("MongoDB Atlas manual setup selected");
      await writeEnvFile(projectDir, backend);
      displayManualSetupInstructions();
      return;
    }

    const mode = await select({
      message: "MongoDB Atlas setup: choose mode",
      options: [
        {
          label: "Automatic",
          value: "auto",
          hint: "Automated setup with provider CLI, sets .env",
        },
        {
          label: "Manual",
          value: "manual",
          hint: "Manual setup, add env vars yourself",
        },
      ],
      initialValue: "auto",
    });

    if (isCancel(mode)) return exitCancelled("Operation cancelled");

    if (mode === "manual") {
      log.info("MongoDB Atlas manual setup selected");
      await writeEnvFile(projectDir, backend);
      displayManualSetupInstructions();
      return;
    }

    const config = await initMongoDBAtlas(serverDir);

    if (config) {
      await writeEnvFile(projectDir, backend, config);
      log.success(pc.green("MongoDB Atlas setup complete! Connection saved to .env file."));
    } else {
      log.warn(pc.yellow("Falling back to local MongoDB configuration"));
      await writeEnvFile(projectDir, backend);
      displayManualSetupInstructions();
    }
  } catch (error) {
    consola.error(
      pc.red(
        `Error during MongoDB Atlas setup: ${
          error instanceof Error ? error.message : String(error)
        }`,
      ),
    );

    try {
      await writeEnvFile(projectDir, backend);
      displayManualSetupInstructions();
    } catch {}
  }
}
